package zzps.exeed.kernel.awareness;

import zzps.exeed.kernel.aop.Aspect;
import zzps.exeed.kernel.aop.InvocationHandlerImpl;
import zzps.exeed.kernel.core.PandaContainer;
import zzps.exeed.kernel.core.PandaEmbryo;
import zzps.exeed.kernel.holder.MethodHolder;
import zzps.exeed.kernel.holder.PandaHolder;
import zzps.exeed.kernel.tool.AnnotationTool;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AspectAwarenessProcessor implements AwarenessProcessor{


    @Override
    public <T> void beforeRegister(PandaHolder<T> pandaHolder, PandaContainer pandaContainer) {
        if (Aspect.class.isAssignableFrom(pandaHolder.clazz())){
            return;
        }
        List<PandaHolder<Aspect>> aspectList = pandaContainer.getAspectList();
        Class<?> clazz = pandaHolder.clazz();
        Annotation[] annotations = AnnotationTool.getAnnotations(clazz);
        boolean needProxy = false;
        for (PandaHolder<Aspect> ph : aspectList) {
            Aspect aspect = ph.get();
            Class<? extends Annotation>[] pointcut = aspect.pointcut();
            boolean b = Arrays.stream(pointcut).anyMatch(p -> Arrays.stream(annotations).anyMatch(annotation -> annotation.annotationType() == p));
            if (b){
                //类注解，所有方法代理
                //proxy
                needProxy = true;
                Map<Method, MethodHolder> methodedHolderMap = pandaHolder.methodHolderMap();
                if (methodedHolderMap == null){
                    methodedHolderMap = new HashMap<>(32);
                    pandaHolder.methodHolderMap(methodedHolderMap);
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    methodedHolderMap.computeIfAbsent(method,key -> new MethodHolder(pandaHolder, method)).addAspect(aspect);
                }
            }else {
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    Annotation[] methodAnnotations = AnnotationTool.getAnnotations(method);
                    b = Arrays.stream(pointcut).anyMatch(p -> Arrays.stream(methodAnnotations).anyMatch(annotation -> annotation.annotationType() == p));
                    if (b){
                        //proxy
                        needProxy = true;
                        Map<Method, MethodHolder> methodedHolderMap = pandaHolder.methodHolderMap();
                        if (methodedHolderMap == null){
                            methodedHolderMap = new HashMap<>(32);
                            pandaHolder.methodHolderMap(methodedHolderMap);
                        }
                        methodedHolderMap.computeIfAbsent(method,key -> new MethodHolder(pandaHolder, method)).addAspect(aspect);
                    }
                }
            }
        }
        if (needProxy){
            try {
                T proxy = pandaContainer.getProxyFactory().createProxy(pandaHolder, new InvocationHandlerImpl(pandaHolder.get(),pandaHolder.methodHolderMap()));
                pandaHolder.set(proxy);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public <T> void afterRegister(PandaHolder<T> pandaHolder) {
    }
}
